﻿package dut.ir.lxb.weibo.spider;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.httpclient.Cookie;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.HttpMethod;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager;
import org.apache.commons.httpclient.NameValuePair;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.params.DefaultHttpParams;
import org.apache.commons.httpclient.params.HttpMethodParams;
import org.json.JSONException;
import org.json.JSONObject;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;

import dut.ir.lxb.weibo.util.BaiduEncoder;
import dut.ir.lxb.weibo.util.SinaSSOEncoder;
import dut.ir.lxb.weibo.util.SpiderConfig;

public class LoginUser
{
	private String userName;
	
	private String password;
	
	
	private String accountType = "baidu";
	
	private String loginUrl;
	
	private String parameterUrl;
	
	private SinaSSOEncoder sinaEncoder;
	
	private BaiduEncoder baiduEncoder;
	
	public LoginUser()
	{
		userName = SpiderConfig.getUserName();
		password = SpiderConfig.getPassword();
		loginUrl = SpiderConfig.getLoginUrl();
		accountType = SpiderConfig.getAccountType();
		parameterUrl = SpiderConfig.getParameterUrl();
		sinaEncoder = new SinaSSOEncoder();
		String baiduJsFile = SpiderConfig.getBaiduJsFile();
		baiduEncoder = new BaiduEncoder(baiduJsFile);
	}
	
	public LoginUser(String username, String password)
	{
		this();
		this.userName = username;
		this.password = password;
	}
	
	
	public HttpClient initHttpClient()
	{
		MultiThreadedHttpConnectionManager connectionManager = new MultiThreadedHttpConnectionManager();
		HttpClient httpClient = new HttpClient(connectionManager);
		
		DefaultHttpParams.getDefaultParams().setParameter("http.protocol.cookie-policy", org.apache.commons.httpclient.cookie.CookiePolicy.BROWSER_COMPATIBILITY);
		httpClient.getParams().setCookiePolicy(org.apache.commons.httpclient.cookie.CookiePolicy.BROWSER_COMPATIBILITY);
		DefaultHttpParams.getDefaultParams().setBooleanParameter(HttpMethodParams.SINGLE_COOKIE_HEADER, true);
		httpClient.getParams().setParameter(HttpMethodParams.USER_AGENT,"Mozilla/5.0 (Windows NT 10.0; WOW64; rv:45.0) Gecko/20100101 Firefox/45.0");  
		httpClient.getHttpConnectionManager().getParams().setConnectionTimeout(60000);
		httpClient.getParams().setParameter("http.socket.timeout", new Integer(60000));
		
		return httpClient;
	}
	
	public Map<String, String> getLoginParameters(String response)
	{
		Map<String, String> parameterMap = new HashMap<String, String>();
		
		JSONObject jsonObject;
		try
		{
			jsonObject = new JSONObject(response.substring(response.indexOf('(') + 1, response.indexOf(')')));
			parameterMap.put("servertime", jsonObject.getString("servertime"));
			parameterMap.put("nonce", jsonObject.getString("nonce"));
			parameterMap.put("pubkey", jsonObject.getString("pubkey"));
			parameterMap.put("rsakv", jsonObject.getString("rsakv"));
		}
		catch (JSONException e)
		{
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		catch (Exception e)
		{
			e.printStackTrace();
		}
		
		
		return parameterMap;
	}
	
	
	/**
	 * 封装登录百度账号的参数
	 * @param parameters
	 * @param postMethod
	 */
	public void wrapBaiduLoginPostParameters(Map<String, String> parameters, PostMethod postMethod)
	{
		List<NameValuePair> parameterList = new ArrayList<NameValuePair>();
		parameterList.add(new NameValuePair("apiver", "v3"));
		parameterList.add(new NameValuePair("callback", "parent.bd__pcbs__rojm4c"/*"parent.bd__pcbs__avv1a8"*/));
		parameterList.add(new NameValuePair("charset", "UTF-8"));
		parameterList.add(new NameValuePair("codestring", ""));
		parameterList.add(new NameValuePair("countrycode", ""));
		parameterList.add(new NameValuePair("crypttype", "12"));
		parameterList.add(new NameValuePair("detect", "1"));
		parameterList.add(new NameValuePair("gid", parameters.get("gid")));
		parameterList.add(new NameValuePair("idc", ""));
		parameterList.add(new NameValuePair("isphone", ""));
		parameterList.add(new NameValuePair("logLoginType", "pc_loginDialog"));
		parameterList.add(new NameValuePair("loginmerge", "true"));
		parameterList.add(new NameValuePair("logintype", "dialogLogin"));
		parameterList.add(new NameValuePair("mem_pass", "on"));
		
		String pubKey = parameters.get("pubkey");
		String pwd = parameters.get("password");
		String password = baiduEncoder.encryptRSAPassword(pwd, pubKey);
		parameterList.add(new NameValuePair("password", password));

		
		parameterList.add(new NameValuePair("ppui_logintime", "12306"/*"38914272"*/));
		parameterList.add(new NameValuePair("quick_user", "0"));
		String rsaKey = parameters.get("rsakey");
		parameterList.add(new NameValuePair("rsakey", rsaKey));
		parameterList.add(new NameValuePair("safeflg", "0"));
		parameterList.add(new NameValuePair("splogin", "rate"));
		parameterList.add(new NameValuePair("staticpage", "http://developer.baidu.com/static/developer3/html/v3Jump.html"));
		parameterList.add(new NameValuePair("subpro", ""));
		String token = parameters.get("token");
		parameterList.add(new NameValuePair("token", token));
		parameterList.add(new NameValuePair("tpl", "dev"));
		String tt = (new Date()).getTime() + "";
		parameterList.add(new NameValuePair("tt", tt));
		parameterList.add(new NameValuePair("u", "http://openapi.baidu.com/"));
		parameterList.add(new NameValuePair("username", parameters.get("username")));
		parameterList.add(new NameValuePair("varifycode", ""));

		
		NameValuePair [] nameValuePairArray = new NameValuePair[parameterList.size()];
		parameterList.toArray(nameValuePairArray);
	
		postMethod.setRequestBody(nameValuePairArray);


	}
	
	public void wrapSinaWeiboLoginPostParameters(Map<String, String> parameters, PostMethod postMethod)
	{
		String data = parameters.get("servertime");
        String nonce = parameters.get("nonce");
        String rsakv = parameters.get("rsakv");
        String encodePwd = new SinaSSOEncoder().getPassword(password, parameters);
        
        List<NameValuePair> nameValuePairList = new ArrayList<NameValuePair>();
		nameValuePairList.add(new NameValuePair("entry", "account"));
		nameValuePairList.add(new NameValuePair("gateway", "1"));
		nameValuePairList.add(new NameValuePair("from", ""));
		nameValuePairList.add(new NameValuePair("savestate", "30"));
		nameValuePairList.add(new NameValuePair("useticket", "0"));
		nameValuePairList.add(new NameValuePair("pagerefer", "http%3A%2F%2Flogin.sina.com.cn%2Fsso%2Flogout.php"));
		nameValuePairList.add(new NameValuePair("vsnf", "1"));
		nameValuePairList.add(new NameValuePair("su", sinaEncoder.encodeAccount(userName)));
		nameValuePairList.add(new NameValuePair("service", "sso"));
		nameValuePairList.add(new NameValuePair("servertime", data));
		nameValuePairList.add(new NameValuePair("nonce", nonce));
		nameValuePairList.add(new NameValuePair("pwencode", "rsa2"));
		nameValuePairList.add(new NameValuePair("rsakv", rsakv));
		nameValuePairList.add(new NameValuePair("sp", encodePwd));
		nameValuePairList.add(new NameValuePair("sr", "1440*900"));
		nameValuePairList.add(new NameValuePair("encoding", "UTF-8"));
		nameValuePairList.add(new NameValuePair("prelt", "31"));
		nameValuePairList.add(new NameValuePair("callback", "parent.sinaSSOController.loginCallBack"));
		nameValuePairList.add(new NameValuePair("returntype", "IFRAME"));
		nameValuePairList.add(new NameValuePair("setdomain", "1"));
		
		NameValuePair [] nameValuePairArray = new NameValuePair[nameValuePairList.size()];
		nameValuePairList.toArray(nameValuePairArray);
	
		postMethod.setRequestBody(nameValuePairArray);
	}
	
	/**
	 * 登出
	 * @param loginClient
	 * @return
	 */
	public boolean logout(HttpClient loginClient)
	{
		if(loginClient == null)
			return false;
		String logoutUrl = SpiderConfig.getLogoutUrl();
		GetMethod getMethod = new GetMethod(logoutUrl);
		GetMethod logoutMethod = new GetMethod("http://passport.weibo.com/wbsso/logout");
		try
		{
			loginClient.executeMethod(getMethod);
			//System.out.println(new String(getMethod.getResponseBody(), "gbk"));
			getMethod.releaseConnection();
			
			loginClient.executeMethod(logoutMethod);
			//System.out.println(new String(logoutMethod.getResponseBody(), "gbk"));
			logoutMethod.releaseConnection();
		}
		catch (HttpException e)
		{
			// TODO Auto-generated catch block
			e.printStackTrace();
		} 
		catch (IOException e)
		{
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return true;
	}
	
	public HttpClient sinaWeiboLogin()
	{
		HttpClient httpClient = initHttpClient();
		String pmUrl= String.format(this.parameterUrl, sinaEncoder.encodeAccount(userName));
		PostMethod postMethod = new PostMethod(loginUrl);
		GetMethod loginConfigMethod = null;
		GetMethod parameterMethod = new GetMethod(pmUrl);
		try
		{
			//发送Get请求获得参数
			httpClient.executeMethod(parameterMethod);
			String parameterString = parameterMethod.getResponseBodyAsString();
			Map<String, String> paraMap = getLoginParameters(parameterString);
			wrapSinaWeiboLoginPostParameters(paraMap, postMethod);
			//发送登录POST请求
			httpClient.executeMethod(postMethod);
			
			String responseString = postMethod.getResponseBodyAsString();
			String jsonString = "";
			try
			{
				jsonString = responseString.substring(responseString.indexOf("setCrossDomainUrlList(") + "setCrossDomainUrlList(".length(),responseString.indexOf(");parent.sinaSSOController"));
			}
			catch (Exception e)
			{
				return null;
			}
			if(jsonString.equals(""))
				return null;
			JSONObject jsonObject = new JSONObject(jsonString);
    		String loginConfigUrl = jsonObject.getJSONArray("arrURL").getString(0);
			loginConfigMethod = new GetMethod(loginConfigUrl);
			
			//发送登录确认请求
			httpClient.executeMethod(loginConfigMethod);
			String loginConRsp = loginConfigMethod.getResponseBodyAsString();
			loginConRsp = loginConRsp.substring(loginConRsp.indexOf('(') + 1, loginConRsp.indexOf(')'));	
			//System.out.println(loginConfigMethod.getResponseBodyAsString());
    		jsonObject = new JSONObject(loginConRsp);
    		boolean loginResult = jsonObject.getBoolean("result");
    		if(loginResult)
    		{
    			System.out.println(String.format("恭喜你%s，登录成功！", userName));
    		}
    		else
    		{
    			System.out.println(String.format("对不起%s，登录失败！", userName));
    			return null;
    		}
		}
		catch (HttpException e)
		{
			// TODO Auto-generated catch block
			e.printStackTrace();
			return null;
		} 
		catch (IOException e)
		{
			// TODO Auto-generated catch block
			e.printStackTrace();
			return null;
		}
		catch (JSONException e)
		{
			e.printStackTrace();
			return null;
		}
		catch (Exception e)
		{
			e.printStackTrace();
			return null;
		}
		finally
		{
			parameterMethod.releaseConnection();
			postMethod.releaseConnection();
			if(loginConfigMethod != null)
				loginConfigMethod.releaseConnection();
		}
		
		return httpClient;
	}
	
	/**
	 * 百度预登录,获得首次加载页面的cookie
	 * @param client
	 */
	public void loadPage(HttpClient client)
	{
		String url = "https://passport.baidu.com/v2/?login";
		HttpMethod getMethod = new GetMethod(url);
		try
		{
			client.executeMethod(getMethod);
//			String jsonStr = getMethod.getResponseBodyAsString();
			//System.out.println(jsonStr);
			
		} 
		catch (HttpException e)
		{
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		catch (IOException e)
		{
			// TODO Auto-generated catch block
			e.printStackTrace();
		}finally
		{
			getMethod.releaseConnection();
		}
	}
	
	/**
	 * 百度登录先获得Token
	 * @param gid 随机ID
	 * @return
	 */
	public String getToken(HttpClient client, String gid)
	{
		String tt = "" + (new Date()).getTime();
		
		String baseUrl = "https://passport.baidu.com/v2/api/?getapi&tpl=dev&apiver=v3&tt=%s&class=login&gid=%s&logintype=dialogLogin&callback=bd__cbs__bsbrf9";
		String requestUrl = String.format(baseUrl, tt, gid);
//		System.out.println(requestUrl);
		
		HttpMethod getMethod = new GetMethod(requestUrl);
		try
		{
			client.executeMethod(getMethod);
			if(!(getMethod.getStatusCode() == HttpStatus.SC_OK)) return "";
			String jsonStr = getMethod.getResponseBodyAsString();
//			System.out.println(jsonStr);
			jsonStr = jsonStr.substring(jsonStr.indexOf('(') + 1, jsonStr.lastIndexOf(')'));
			
			JSONObject jsonObj = new JSONObject(jsonStr);
			String token = new JSONObject(jsonObj.getString("data")).getString("token").toString();
//			System.out.println("token:" + token);
			return token;
		} 
		catch (HttpException e)
		{
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		catch (IOException e)
		{
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (JSONException e)
		{
			// TODO Auto-generated catch block
			e.printStackTrace();
		}finally
		{
			getMethod.releaseConnection();
		}
		
		return "";
	}
	
	/**
	 * 百度登录获得RSA加密公钥
	 * @param client
	 * @param gid
	 * @param token
	 * @return
	 */
	public String[] getRSAKeyAndPubKey(HttpClient client, String gid, String token)
	{
		String tt = "" + (new Date()).getTime();
		
		String baseUrl = "https://passport.baidu.com/v2/getpublickey?token=%s&tpl=dev&apiver=v3&tt=%s&gid=%s&callback=bd__cbs__fbzosu";
				//"https://passport.baidu.com/v2/api/?getapi&tpl=dev&apiver=v3&tt=%s&class=login&gid=%s&logintype=dialogLogin&callback=bd__cbs__bsbrf9";
		String requestUrl = String.format(baseUrl, token, tt, gid);
		
		HttpMethod getMethod = new GetMethod(requestUrl);
		try
		{
			client.executeMethod(getMethod);
			if(!(getMethod.getStatusCode() == HttpStatus.SC_OK)) return null;
			String jsonStr = getMethod.getResponseBodyAsString();
			jsonStr = jsonStr.substring(jsonStr.indexOf('(') + 1, jsonStr.lastIndexOf(')'));
			
			JSONObject jsonObj = new JSONObject(jsonStr);
			String rsaKey = jsonObj.getString("key");
			String pubKey = jsonObj.getString("pubkey");
			
			return new String[]{rsaKey, pubKey};
		} 
		catch (HttpException e)
		{
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		catch (IOException e)
		{
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (JSONException e)
		{
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		finally
		{
			getMethod.releaseConnection();
		}
		
		return null;
		
		
	}
	
	/**
	 * 通过Cookies检查百度账号是否登录成功
	 * @param client
	 * @return
	 */
	public boolean checkBaiduLoginResult(HttpClient client)
	{
		Cookie[] cookies = client.getState().getCookies();
		if(cookies != null)
		{
			for (Cookie cookie: cookies)
				if(cookie.getName().equals("BDUSS")) return true;
		}
		return false;
		
//		String homePage = "http://www.baidu.com";
//		GetMethod method = new GetMethod(homePage);
//		try
//		{
//			client.executeMethod(method);
//			byte [] bytes = method.getResponseBody();
//			String respStr = new String(bytes, "UTF-8");
//			if(respStr.contains(userName))
//				return true;
//			else
//				return false;
//			
//		} catch (HttpException e)
//		{
//			// TODO Auto-generated catch block
//			e.printStackTrace();
//		} catch (IOException e)
//		{
//			// TODO Auto-generated catch block
//			e.printStackTrace();
//		}
//		finally
//		{
//			method.releaseConnection();
//		}
//		return false;
	}
	
	/**
	 * 百度账号登录
	 * @return
	 */
	public HttpClient baiduLogin()
	{
		HttpClient httpClient = initHttpClient();
		
		loadPage(httpClient);
		
		String gid = baiduEncoder.randomGuid();
		Map<String, String> parameters = new HashMap<String, String>();
		
		parameters.put("gid", gid);
		
		String token = getToken(httpClient, gid);
		parameters.put("token", token);
		
		String [] rsa = getRSAKeyAndPubKey(httpClient, gid, token);
		if(rsa == null) return null;
		parameters.put("rsakey", rsa[0]);
		parameters.put("pubkey", rsa[1]);
		parameters.put("username", userName);
		parameters.put("password", password);
		
		String requestUrl= "https://passport.baidu.com/v2/api/?login";
		PostMethod loginMethod = new PostMethod(requestUrl);
		wrapBaiduLoginPostParameters(parameters, loginMethod);
		
		try
		{
			//发送登录请求
			httpClient.executeMethod(loginMethod);
			
			//check login result
    		boolean loginResult = checkBaiduLoginResult(httpClient);
			
    		if(loginResult)
    		{
    			System.out.println(String.format("恭喜你百度账号%s，登录成功！", userName));
    			//请求新浪微博账号绑定页面
    		}
    		else
    		{
    			System.out.println(String.format("对不起%s，登录失败！", userName));
    			return null;
    		}
		}
		catch (HttpException e)
		{
			// TODO Auto-generated catch block
			e.printStackTrace();
			return null;
		} 
		catch (IOException e)
		{
			// TODO Auto-generated catch block
			e.printStackTrace();
			return null;
		}
		catch (Exception e)
		{
			e.printStackTrace();
			return null;
		}
		finally
		{
			loginMethod.releaseConnection();
		}
		
		return httpClient;
	}
	
	
	public HttpClient login()
	{
		
		if(accountType.toLowerCase().equals("baidu"))
		{
			HttpClient client = baiduLogin();
			
			if(client != null)
			{
				//请求绑定账号登录页面
				String requestUrl = "https://passport.weibo.com/othersitebind/authorize?entry=miniblog&site=baidu";
				GetMethod method = new GetMethod(requestUrl);
				GetMethod redirectMethod = null;
				GetMethod confirmMethod = null;
				try
				{
					client.executeMethod(method);
					if(method.getStatusCode() == HttpStatus.SC_OK)
					{
						byte[] bytes = method.getResponseBody();
						String responseStr = new String(bytes, "GB2312");
						Document doc = 	Jsoup.parse(responseStr);
						//请求重定向，获取新浪通行证
						String redirectUrl = doc.getElementsByTag("script").get(0).toString();
						redirectUrl = redirectUrl.substring(redirectUrl.indexOf("location.replace(\"") + 18, redirectUrl.lastIndexOf('"'));
//						System.out.println("redirect url: " + redirectUrl);
		
						redirectMethod = new GetMethod(redirectUrl);
						client.executeMethod(redirectMethod);
						
						//登录确认
						String loginConfirmUrl = new String(redirectMethod.getResponseBody(), "GB2312");
						loginConfirmUrl = Jsoup.parse(loginConfirmUrl).getElementsByTag("script").get(1).toString();
						loginConfirmUrl = loginConfirmUrl.substring(loginConfirmUrl.indexOf("location.replace('") + 18, loginConfirmUrl.indexOf("');});}"));
						
						confirmMethod = new GetMethod(loginConfirmUrl);
						client.executeMethod(confirmMethod);
						
						
//						String homeUrl = "http://weibo.com/lianxubao/home";
//						GetMethod homeMethod = new GetMethod(homeUrl);
//						client.executeMethod(homeMethod);
//						
//						bytes = homeMethod.getResponseBody();
//						System.out.println(new String(bytes, "UTF-8"));
						
					}
				} catch (HttpException e)
				{
					// TODO Auto-generated catch block
					e.printStackTrace();
				} catch (IOException e)
				{
					// TODO Auto-generated catch block
					e.printStackTrace();
				}finally
				{
					method.releaseConnection();
					if(redirectMethod != null)
						redirectMethod.releaseConnection();
					if(confirmMethod != null)
						confirmMethod.releaseConnection();
				}
			}
			
			return client;
		}
		else
			return sinaWeiboLogin();
		
		
	}
	
	
	

	public String getUserName()
	{
	
		return userName;
	}

	public void setUserName(String userName)
	{
	
		this.userName = userName;
	}

	public String getPassword()
	{
	
		return password;
	}

	public void setPassword(String password)
	{
	
		this.password = password;
	}

	public String getLoginUrl()
	{
	
		return loginUrl;
	}

	public void setLoginUrl(String loginUrl)
	{
	
		this.loginUrl = loginUrl;
	}
	
	public static void main(String [] args)
	{
		LoginUser user = new LoginUser("lianxubao", "138060");
		user.login();
		
		
	}
	
	
}
